// Kaikki TUIO:n callback-metodit
// = addTuioCursor, removeTuioCursor, updateTuioCursor & refresh
// ja suoraan niin liittyvä sälä

void addTuioCursor(TuioCursor tobj) {  
  // First touch = start game
  if(!game.started) {
    if(frameCount > endMsgLength + game.endFrame) {
      game.start();
    }
  } else {
  
    int tX = tobj.getScreenX(width);
    int tY = tobj.getScreenY(height);
    int tCursorID = tobj.getCursorID();
    
    Block selectedBlock = selectedBlock(tobj);
    
    // Add vertice to a new or existing Block
    if(selectedBlock == null) {
      if(inTeamArea(tX, tY)) {
        Block nearestBlock = findBlockFor(tX, tY);
       
        if(nearestBlock != null) {
          // Existing Block
          nearestBlock.add(tX,tY,tCursorID);
        } else {
          // Create new Block
          BlockFactory bf = new BlockFactory();
          Block b = bf.createBlock(-1);
          b.add((int)tX, (int)tY, tobj.getCursorID());
          if(inLeftTeamArea(tX, tY)) {
              game.blocksTeamA.add(b);
          } else if (inRightTeamArea(tX, tY)) {
              game.blocksTeamB.add(b);  
          }
        }
      }
    } else {
      // If new cursor is on a completed Block, set it selected
       selectedBlock.selected = tobj;
    }
  
  }
}

void removeTuioCursor(TuioCursor tcur) {
  
  if(game.started) {
  
    int tX = tcur.getScreenX(width);
    int tY = tcur.getScreenY(height);
    
    Block selectedBlock = selectedBlock(tcur);
    
    if(selectedBlock != null ) {
      // Throw a completed Block when cursor removed from on top of it
      Block b = selectedBlock;
      b. selected = null;
      
      float xPosDiff = 0, yPosDiff = 0;
      float timeDiff = 0;
      
      int pathThreshold = 3;
      if(tcur.getPath().size() > pathThreshold) {
        Vector<TuioPoint> path = tcur.getPath();
        PVector speed = getThrowSpeed(path);      
        b.setSpeed(speed);
      }
    } else {
      // If cursor belonged to an incomplete Block, remove the vertice
      for(Block b : game.getIncompleteBlocks()) {
        if(b.contains(tcur.getCursorID())) {
          b.remove(tcur.getCursorID());  
          
          if(b.size() == 0) {
            game.blocksTeamA.remove(b);
            game.blocksTeamB.remove(b);
          }    
        }
      }
    }
  }
}

void updateTuioCursor(TuioCursor tcur) {
  
  if(game.started) {
  
    int tX = tcur.getScreenX(width);
    int tY = tcur.getScreenY(height);
    int tCursorID = tcur.getCursorID();
    
    Block selectedBlock = selectedBlock(tcur);
    
    if(selectedBlock != null) {
      // Move completed Block if cursor is on top of it
      
      float bX=0, bY=0;
      int counter=0;
      for(int j=0; j < selectedBlock.size(); j++) {
        //PVector v = selectedBlock.vertices.get(j);     
        bX = bX + (int)selectedBlock.vertices.get(j).x;
        bY = bY + (int)selectedBlock.vertices.get(j).y;
        counter++;
      }
      bX = bX/counter;
      bY = bY/counter;
      selectedBlock.translate((int)(tX-bX), (int)(tY-bY));
    } else {
      // Update the incomplete Block corresponding to the cursor
      for(Block b : game.getIncompleteBlocks()) {
        for(int j=0; j < b.size(); j++) {
          if((b.vertices.get(j).z == tcur.getCursorID() )) {
            b.update((int)tcur.getScreenX(width), 
                     (int)tcur.getScreenY(height), tcur.getCursorID());
            break;
          }
        }
      }
    }
  }
}

void refresh(TuioTime bundleTime) { 
  //redraw();
}

// Helper methods for the previous

// Checks if given cursor has selected a Block
// Returns this block if true, null if no such Block found
Block selectedBlock(TuioCursor tcur) {
  int tX = tcur.getScreenX(width);
  int tY = tcur.getScreenY(height);
  for(int i=0; i < game.completedBlocks.size(); i++) {
    Block b = game.completedBlocks.get(i);
    if(b.insideHull(tX, tY) || b.selected == tcur) {
      return game.completedBlocks.get(i);
    }
  }
 return null;
}

// Checks if given coordinates are near an incomplete Block
// Returns this block if true, null if no such Block found
Block findBlockFor(int tX, int tY) {
  for(Block b : game.getIncompleteBlocks()) {
      CopyOnWriteArrayList<PVector> vertices = b.vertices;
      float x = 0.0;
      float y = 0.0;
      int counter = 0;
      for(int j=0; j < vertices.size(); j++) {  
        x += vertices.get(j).x;
        y += vertices.get(j).y;
        counter++;
      }
      x = x/counter;
      y = y/counter; 
      if(dist(x, y, tX, tY) < game.blockSize) {
        return b;
      }
    }
    return null;
}

// Calculate speed relative to multiple points in path & get weighted mean
PVector getThrowSpeed(Vector<TuioPoint> path) {
  int n = path.size();
  
  // Number of sample points and sample spacing
  int sampleN = 5;
  if(sampleN > n*2) return new PVector(0,0,0);
  double sampleS = n / (double)sampleN;
  
  TuioPoint endP = path.get(n - 1);
  TuioPoint sampleP;
  PVector sampleSpeed;
  
  float finalX = 0;
  float finalY = 0;
  int weight = (sampleN + 1);
  int meanDivisor = 0;
  
  // (i=0 -> endP)
  //println(sampleN + " " + n);
  for(int i = 1; i <= sampleN; i++) {
    //print(i);
    sampleP = path.get((int)(n - 1 - i*(sampleS-1)));
    sampleSpeed = getThrowSpeedAt(sampleP, endP);

    // Add to weighted average
    finalX += sampleSpeed.x * weight;
    finalY += sampleSpeed.y * weight;
    
    meanDivisor += weight;
    weight --;
  }
  
  // Divide weighted sum of speeds for the result
  return new PVector(finalX/meanDivisor, finalY/meanDivisor);
}

// Returns simple speed between two TuioPoints
// Can be used to calculate simple but effective throw if
//  endP = last point in path and startP = first point in path
PVector getThrowSpeedAt(TuioPoint startP, TuioPoint endP) {
  float timeDiff = endP.getTuioTime().getTotalMilliseconds() 
                   - startP.getTuioTime().getTotalMilliseconds();
  float xPosDiff = (float) endP.getScreenX(width) - startP.getScreenX(width);
  float yPosDiff = (float) endP.getScreenY(height) - startP.getScreenY(height);
  
  float moveX = xPosDiff / timeDiff * 40;
  float moveY = yPosDiff / timeDiff * 40;
   
  return new PVector(moveX, moveY);
}

// Objects not used in game, dummy methods to avoid warnings below

void addTuioObject(TuioObject tobj) {
}
void removeTuioObject(TuioObject tobj) {
}
void updateTuioObject(TuioObject tobj) {
}
